﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Atlepage.Lexical;
using System.Text.RegularExpressions;
using Atlepage.Syntactic;
using Atlepage;

namespace Test
{
    [Start("STATEMENT")]
    class MiniLang
    {
        public enum TokenKind
        {
            UNKNOWN,
            EXIT,
            PRINT,
            ID,
            NUMBER,
            OP,
            ASSIGN,
            OPAREN,
            CPAREN,
            SEMI,
        }

        public class T : Atlepage.Token<T,GenericEnum<TokenKind>>
        {
            public double NumericValue { get; set; }
        }
        Lexer<T, GenericEnum<TokenKind>> lexer;
        Parser parser;

        Dictionary<string, double> vars = new Dictionary<string, double>();

        public MiniLang()
        {
            lexer = new Lexer<T, GenericEnum<TokenKind>>(this);
            ParserFactory tg = new ParserFactory();
            List<string> errors = new List<string>();
            tg.Generate(typeof(MiniLang), new GenericEnum<TokenKind>(), errors, errors);
            parser = tg.CreateParser(this);
            Console.WriteLine(parser);
        }

        public void Run()
        {
            while (true)
            {
                string line = Console.ReadLine();
                lexer.Begin(line);
                parser.Parse(lexer);
            }
        }

        [Token("[a-zA-Z][a-zA-Z0-9]*")]
        public T t_ID(Group g, T t)
        {
            if (t.Value == "exit")
                t.Type = (int)TokenKind.EXIT;
            else if (t.Value == "print")
                t.Type = (int)TokenKind.PRINT;
            return t;
        }

        [Token(@"(\+|-)?[0-9]+(\.[0-9]+)?")]
        public T t_NUMBER(Group g, T t)
        {
            t.NumericValue = double.Parse(t.Value);
            return t;
        }

        public string t_OP = @"\+|-|/|\*|%";
        public string t_ASSIGN = "=";
        public string t_OPAREN = @"\(";
        public string t_CPAREN = @"\)";
        public string t_SEMI = ";";

        [Token(@" |\t")]
        public T t_WS(Group g, T t)
        {
            return null;
        }

        [Rule("ID ASSIGN E SEMI")]
        public double p_STATEMENT(T id, T assign, double e, T semi)
        {
            vars[id.Value] = e;
            return e;
        }

        [Rule("PRINT OPAREN E CPAREN SEMI")]
        public double p_STATEMENT(T print, T oparen, double e, T cparen, T semi)
        {
            Console.WriteLine(e);
            return e;
        }

        [Rule("EXIT")]
        public void p_STATEMENT(T exit)
        {
            Environment.Exit(0);
        }

        [Rule(@"E OP T")]
        public double p_E(double e, T op, double t)
        {
            switch (op.Value)
            {
                case "+":
                    return e + t;
                case "-":
                    return e - t;
                case "/":
                    return e / t;
                case "%":
                    return Math.IEEERemainder(e, t);
                case "*":
                    return e * t;
                default:
                    throw new InvalidProgramException();
            }
        }

        [Rule("T")]
        public double p_E(double t)
        {
            return t;
        }

        [Rule("NUMBER", Name="T")]
        public double p_T_Number(T n)
        {
            return n.NumericValue;
        }

        [Rule("ID", Name="T")]
        public double p_T_Variable(T id)
        {
            double r;
            if (!vars.TryGetValue(id.Value, out r))
                r = 0;
            return r;
        }

        [Rule("OPAREN E CPAREN")]
        public double p_E(T oparen, double e, T cparen)
        {
            return e;
        }
    }

    [Start("E")]
    public class SimpleWikiPediaParser
    {
        public enum Tokens
        {
            STAR,
            PLUS,
            ONE,
            ZERO,
        }

        public SimpleWikiPediaParser()
        {
            List<string> info = new List<string>();
            ParserFactory tg = new ParserFactory();
            tg.Generate(typeof(SimpleWikiPediaParser), new GenericEnum<Tokens>(), info, info);
            Console.Write(tg.CreateParser(this));
        }

        [Rule("E STAR B | E PLUS B")]
        public int p_E(int e, TokenBase op, int b)
        {
            if (op.Type == (int)Tokens.STAR)
                return e * b;
            else
                return e + b;
        }
        [Rule("B")]
        public int p_E(int b)
        {
            return b;
        }
        [Rule("ONE | ZERO")]
        public int p_B(TokenBase d)
        {
            if (d.Type == (int)Tokens.ONE)
                return 1;
            else
                return 0;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SimpleWikiPediaParser swpp = new SimpleWikiPediaParser();

            MiniLang ml = new MiniLang();
            ml.Run();
        }
    }
}
